package com.douban.api
{
	import com.adobe.crypto.HMAC;
	import com.adobe.crypto.SHA1;
	import com.adobe.serialization.json.JSONDecoder;
	import com.adobe.utils.Base64;
	import com.douban.utils.DoubanConfig;
	import com.utils.GUID;
	import com.utils.StringEncoders;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	
	/**
	 * 当数据加载完成，派发这个事件，您可以通过data属性获取数据
	 */
	[Event(name="complete", type="flash.events.Event")]
	/**
	 * 数据加载过程中，派发这个事件，您可以了解加载进度
	 */
	[Event(name="progress", type="flash.events.ProgressEvent")]
	/**
	 * 当数据加载失败，则派发这个事件
	 */
	[Event(name="ioError", type="flash.events.IOErrorEvent")]
	
	public class AbstractAPI_DB extends EventDispatcher
	{
		public function AbstractAPI_DB()
		{
			config = DoubanConfig.getInstance();			
		}
		
		private var _urlLoader:URLLoader;
		private var _urlRequest:URLRequest;
		private var _data:Object;
		public var config:DoubanConfig;
		
		public function get data():Object{
			return _data;
		}
		
		public function set data(value:Object):void{
			_data = value;
		}
		
		public function get urlLoader():URLLoader{
			return _urlLoader;
		}
		public function set urlLoader(value:URLLoader):void{
			_urlLoader = value;
		}
		
		public function loadUrl(url:String,parms:Object,method:String='GET'):void{			
			_urlLoader = new URLLoader();
			//			if(config.dataFormat == 'json')url = url.replace('.xml','.json');
			//			if(url.substr(0,5) != 'http:')url = config.baseURL + url;
			_urlRequest = signRequest(method,url,parms);
			_addEventListener();
			_urlLoader.load(_urlRequest);			
		}
		
		protected function errorHandler(event:IOErrorEvent):void{
			dispatchEvent(event);
			
			_removeEventListener();
			//			_urlLoader.removeEventListener(IOErrorEvent.IO_ERROR,errorHandler);
		}
		
		/**@private*/
		protected function progressHandler(event:ProgressEvent):void{
			dispatchEvent(event);
		}
		
		/**@private*/
		protected function completeHandler(event:Event):void{
			var sourceData:String = event.target.data;
			switch(config.dataFormat){
				case "json":
					try{
						_data = new JSONDecoder(sourceData).getValue();
					}
					catch(err:Error){
						//						logger.error("completeHandler:JSON format unusual: {0}",err.message);
						return;
					}
					break;
				case "xml":
					if(sourceData.indexOf("<") != -1 && sourceData.indexOf(">") != -1){
						try{
							sourceData = sourceData.replace(/<\/>/g,"");
							_data = new XML(sourceData);
						}
						catch(err:Error){
							//							logger.error("completeHandler:XML format unusual: {0} {1}",err.message,sourceData);
							return;
						}
					}
					else{
						//						_format = "json";
						completeHandler(event);
						return;
					}
					break;
				default:
					_data = sourceData;
			}
			_removeEventListener();
			//			_urlLoader.removeEventListener(ProgressEvent.PROGRESS,progressHandler);
			//			_urlLoader.removeEventListener(Event.COMPLETE,completeHandler);
		}
		
		/**
		 * 重复调用
		 */		
		public function repeat():void{
			_urlLoader.load(_urlRequest);
		}
		
		public function signRequest(requestMethod:String, url:String, requestParams:Object, useHead:Boolean=true):URLRequest{			
			var method:String = requestMethod.toUpperCase();
			var oauthParams:Object = getOAuthParams();
			var params:Object = new Object;
			for (var key:String in oauthParams){
				params[key] = oauthParams[key];
			}
			if(null != requestParams){
				for (var key1:String in requestParams){
					params[key1] = requestParams[key1];
				}			
			}
			
			var req:URLRequest=new URLRequest();
			req.method=method;
			req.url = url;
			//			req.authenticate = false;
			var paramsStr:String=makeSignableParamStr(params);
			var msgStr:String=StringEncoders.urlEncodeUtf8String(requestMethod.toUpperCase()) + "&";
			msgStr+=StringEncoders.urlEncodeUtf8String(url);
			msgStr+="&";
			msgStr += StringEncoders.urlEncodeSpecial(paramsStr);	
			var secrectStr:String = config.consumerSecret + "&";
			if (config.accessTokenKey.length > 0 && config.accessTokenSecret.length > 0){
				secrectStr+=config.accessTokenSecret;
			}
			//			trace(loginInfo.accessTokenKey);
			//host 
			var sig:String = Base64.encode(HMAC.hash(secrectStr, msgStr, SHA1));		
			// The matchers are specified in OAuth only.		
			//sig = sig.replace(/\+/g, "%2B"); //////////////////////////////////////////////////////////	
			oauthParams["oauth_signature"] = sig;
			//			oauthParams["host"] = "api.fanfou.com";
			if (method == URLRequestMethod.GET){
				if(null != requestParams)req.url+=("?" + makeSignableParamStr(requestParams));
				req.requestHeaders.push(makeOauthHeaderFromArray(oauthParams));
			}else if (requestMethod == URLRequestMethod.POST) {
				var val:URLVariables = new URLVariables();
				for (var paramkey:* in params){
					val[paramkey] = params[paramkey];
				}
				val.oauth_signature = sig;
				req.data = val;
				if(useHead)req.requestHeaders.push(makeOauthHeaderFromArray(oauthParams));
			}
			//		req.requestHeaders.push(new URLRequestHeader("host","api.fanfou.com"));
			return req;
		}
		
		private function getOAuthParams():Object{
			var params:Object=new Object;
			var now:Date=new Date();
			params["oauth_consumer_key"]=config.consumerKey;
			if (null != config.accessTokenKey && config.accessTokenKey.length > 0)	params["oauth_token"] = config.accessTokenKey;
			params["oauth_signature_method"]="HMAC-SHA1";
			params["oauth_timestamp"]=now.time.toString().substr(0, 10);
			params["oauth_nonce"]=GUID.createGUID();
			params["oauth_version"]="1.0";
			
			if (config.username != "" && config.password != "")
			{
				params["x_auth_username"] = config.username;
				params["x_auth_password"] = config.password;
				params["x_auth_mode"] = "client_auth";
			}			
			return params;
		}
		
		private function makeSignableParamStr(params:Object):String{
			var retParams:Array=[];
			
			for (var param:String in params){
				if (param != "oauth_signature"){
					if(params[param] != null) retParams.push(param + "=" + StringEncoders.urlEncodeSpecial(params[param].toString()));
				}
			}
			retParams.sort();
			return retParams.join("&");
		}
		private function makeOauthHeaderFromArray(params:Object):URLRequestHeader{
			var oauthHeaderValue:String='OAuth realm="http://api.fanfou.com/",';
			var parseParams:Array=[];
			for (var key:String in params){
				if (key == "oauth_signature"){
					//				trace(StringEncoders.urlEncodeSpecial(params[key]));
					parseParams.push(key + '="' + StringEncoders.urlEncodeSpecial(params[key]) + '"');
				}else{
					parseParams.push(key + '="' + params[key] + '"');						
				}
			}
			oauthHeaderValue+=parseParams.join(",");
			
			var reqHeader:URLRequestHeader=new URLRequestHeader("Authorization", oauthHeaderValue);
			return reqHeader;
		}
		
		public function _removeEventListener():void{
			_urlLoader.removeEventListener(ProgressEvent.PROGRESS,progressHandler);
			_urlLoader.removeEventListener(Event.COMPLETE,completeHandler);
			_urlLoader.removeEventListener(IOErrorEvent.IO_ERROR,errorHandler);
		}
		
		public function _addEventListener():void{
			_urlLoader.addEventListener(ProgressEvent.PROGRESS,progressHandler);
			_urlLoader.addEventListener(Event.COMPLETE,completeHandler);
			_urlLoader.addEventListener(IOErrorEvent.IO_ERROR,errorHandler);
		}
	}
}